package com.example.domain.valueobject;

import com.example.exception.InvalidRequestException;
import lombok.Data;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Currency;
import java.util.Objects;

/**
 * 金额值对象
 * 封装金额和货币类型
 * 
 * @author Means
 * @since 2025-10-27
 */
@Data
public class Money {
    
    /** 默认货币 */
    private static final Currency DEFAULT_CURRENCY = Currency.getInstance("CNY");
    
    /** 默认精度（小数位数） */
    private static final int DEFAULT_SCALE = 2;
    
    /** 计算精度（用于中间计算） */
    private static final int CALCULATION_SCALE = 4;
    
    /** 默认舍入模式：四舍五入 */
    private static final RoundingMode DEFAULT_ROUNDING_MODE = RoundingMode.HALF_UP;
    
    /** 金额值 */
    private final BigDecimal amount;
    
    /** 货币类型 */
    private final Currency currency;
    
    private Money(BigDecimal amount, Currency currency) {
        if (amount == null) {
            throw new InvalidRequestException("金额不能为空");
        }
        if (currency == null) {
            throw new InvalidRequestException("货币类型不能为空");
        }
        this.amount = amount.setScale(CALCULATION_SCALE, DEFAULT_ROUNDING_MODE);
        this.currency = currency;
    }
    
    /**
     * 创建金额对象
     * 
     * @param amount 金额
     * @return 金额对象
     */
    public static Money of(BigDecimal amount) {
        return new Money(amount, DEFAULT_CURRENCY);
    }
    
    /**
     * 创建金额对象
     * 
     * @param amount 金额
     * @param currency 货币类型
     * @return 金额对象
     */
    public static Money of(BigDecimal amount, Currency currency) {
        return new Money(amount, currency);
    }
    
    /**
     * 创建金额对象
     * 
     * @param amount 金额（double）
     * @return 金额对象
     */
    public static Money of(double amount) {
        return new Money(BigDecimal.valueOf(amount), DEFAULT_CURRENCY);
    }
    
    /**
     * 创建金额对象
     * 
     * @param amount 金额（String）
     * @return 金额对象
     */
    public static Money of(String amount) {
        try {
            return new Money(new BigDecimal(amount), DEFAULT_CURRENCY);
        } catch (NumberFormatException e) {
            throw new InvalidRequestException("无效的金额: " + amount);
        }
    }
    
    /**
     * 零金额
     * 
     * @return 零金额对象
     */
    public static Money zero() {
        return new Money(BigDecimal.ZERO, DEFAULT_CURRENCY);
    }
    
    /**
     * 零金额
     * 
     * @param currency 货币类型
     * @return 零金额对象
     */
    public static Money zero(Currency currency) {
        return new Money(BigDecimal.ZERO, currency);
    }
    
    /**
     * 是否为零
     * 
     * @return 是否为零
     */
    public boolean isZero() {
        return amount.compareTo(BigDecimal.ZERO) == 0;
    }
    
    /**
     * 是否为正数
     * 
     * @return 是否为正数
     */
    public boolean isPositive() {
        return amount.compareTo(BigDecimal.ZERO) > 0;
    }
    
    /**
     * 是否为负数
     * 
     * @return 是否为负数
     */
    public boolean isNegative() {
        return amount.compareTo(BigDecimal.ZERO) < 0;
    }
    
    /**
     * 加法
     * 
     * @param other 另一个金额
     * @return 相加结果
     */
    public Money add(Money other) {
        checkCurrencyCompatibility(other);
        return new Money(this.amount.add(other.amount), this.currency);
    }
    
    /**
     * 减法
     * 
     * @param other 另一个金额
     * @return 相减结果
     */
    public Money subtract(Money other) {
        checkCurrencyCompatibility(other);
        return new Money(this.amount.subtract(other.amount), this.currency);
    }
    
    /**
     * 乘法（金额 × 倍数）
     * 
     * @param multiplier 乘数
     * @return 相乘结果
     */
    public Money multiply(BigDecimal multiplier) {
        if (multiplier == null) {
            throw new InvalidRequestException("乘数不能为空");
        }
        return new Money(this.amount.multiply(multiplier), this.currency);
    }
    
    /**
     * 乘法（金额 × 倍数）
     * 
     * @param multiplier 乘数
     * @return 相乘结果
     */
    public Money multiply(double multiplier) {
        return multiply(BigDecimal.valueOf(multiplier));
    }
    
    /**
     * 除法（金额 ÷ 除数）
     * 
     * @param divisor 除数
     * @return 相除结果
     */
    public Money divide(BigDecimal divisor) {
        if (divisor == null || divisor.compareTo(BigDecimal.ZERO) == 0) {
            throw new InvalidRequestException("除数不能为零");
        }
        return new Money(this.amount.divide(divisor, CALCULATION_SCALE, DEFAULT_ROUNDING_MODE), this.currency);
    }
    
    /**
     * 除法（金额 ÷ 除数）
     * 
     * @param divisor 除数
     * @return 相除结果
     */
    public Money divide(double divisor) {
        return divide(BigDecimal.valueOf(divisor));
    }
    
    /**
     * 取绝对值
     * 
     * @return 绝对值
     */
    public Money abs() {
        return isNegative() ? new Money(amount.negate(), currency) : this;
    }
    
    /**
     * 取反
     * 
     * @return 相反数
     */
    public Money negate() {
        return new Money(amount.negate(), currency);
    }
    
    /**
     * 比较大小
     * 
     * @param other 另一个金额
     * @return 比较结果（-1: 小于, 0: 等于, 1: 大于）
     */
    public int compareTo(Money other) {
        checkCurrencyCompatibility(other);
        return this.amount.compareTo(other.amount);
    }
    
    /**
     * 是否大于
     * 
     * @param other 另一个金额
     * @return 是否大于
     */
    public boolean greaterThan(Money other) {
        return compareTo(other) > 0;
    }
    
    /**
     * 是否大于等于
     * 
     * @param other 另一个金额
     * @return 是否大于等于
     */
    public boolean greaterThanOrEqual(Money other) {
        return compareTo(other) >= 0;
    }
    
    /**
     * 是否小于
     * 
     * @param other 另一个金额
     * @return 是否小于
     */
    public boolean lessThan(Money other) {
        return compareTo(other) < 0;
    }
    
    /**
     * 是否小于等于
     * 
     * @param other 另一个金额
     * @return 是否小于等于
     */
    public boolean lessThanOrEqual(Money other) {
        return compareTo(other) <= 0;
    }
    
    /**
     * 获取用于展示的金额（保留2位小数）
     * 
     * @return 展示金额
     */
    public BigDecimal getDisplayAmount() {
        return amount.setScale(DEFAULT_SCALE, DEFAULT_ROUNDING_MODE);
    }
    
    /**
     * 获取用于计算的金额（保留4位小数）
     * 
     * @return 计算金额
     */
    public BigDecimal getCalculationAmount() {
        return amount;
    }
    
    /**
     * 检查货币兼容性
     * 
     * @param other 另一个金额
     */
    private void checkCurrencyCompatibility(Money other) {
        if (!this.currency.equals(other.currency)) {
            throw new InvalidRequestException(
                String.format("货币类型不兼容: %s 和 %s", 
                    this.currency.getCurrencyCode(), other.currency.getCurrencyCode()));
        }
    }
    
    /**
     * 格式化为字符串（带货币符号）
     * 
     * @return 格式化字符串
     */
    public String format() {
        return currency.getSymbol() + getDisplayAmount().toPlainString();
    }
    
    /**
     * 格式化为字符串（不带货币符号）
     * 
     * @return 格式化字符串
     */
    public String formatWithoutSymbol() {
        return getDisplayAmount().toPlainString();
    }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Money money = (Money) o;
        return amount.compareTo(money.amount) == 0 && currency.equals(money.currency);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(amount, currency);
    }
    
    @Override
    public String toString() {
        return format();
    }
}

